import { dom, EventHandler, Nodes } from '@knno/dom';
import { Container, FormItem } from '../interface';
import { DATA } from '../symbol';
import { ExtendEventHandler } from '../../tools/eventDef';

export type FacadeIcons = {
	unchecked?: string;
	checked?: string;
	indeterminate?: string;
};

type CheckableEventMap = {
	update: CustomEvent<{ checked: boolean }>;
};
type M = CheckableEventMap;
export interface Checkable {
	on<K extends keyof M>(
		event: K,
		listener: ExtendEventHandler<M, K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
	on<K extends keyof HTMLElementEventMap>(
		event: K,
		listener: EventHandler<K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
	off<K extends keyof M>(
		event: K,
		listener: ExtendEventHandler<M, K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
	off<K extends keyof HTMLElementEventMap>(
		event: K,
		listener: EventHandler<K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
}

export abstract class Checkable extends Container implements FormItem {
	protected [DATA]?: FacadeIcons;

	protected get box(): Nodes {
		const span = this.query(':scope>span').takeIf() ?? dom.span().appendTo(this.elems());
		return span;
	}

	protected get root(): Nodes {
		return dom.wrap(...this.elems());
	}

	constructor(elements: Iterable<Element | Text>) {
		super(elements);
	}

	abstract checked(): unknown;
	abstract checked(value: unknown): this;

	abstract updateFacade(): void;

	facadeIcons(icons: FacadeIcons) {
		this[DATA] = icons;
		this.updateFacade();
		return this;
	}

	disabled(): boolean;
	disabled(value: boolean): this;
	disabled(value?: boolean): boolean | this {
		if (value === undefined) {
			return this.attr('disabled') != null;
		}
		return this.attr('disabled', value);
	}

	override text(): string;
	override text(text: string): this;
	override text(text?: string): this | string | undefined {
		if (text !== undefined) {
			if (text) {
				const span = this.query(':scope>span').takeIf() ?? dom.span().appendTo(this.elems());
				span.text(text);
			} else {
				this.remove(this.query(':scope>span'));
			}
			return this;
		}
		return this.query(':scope>span').text();
	}
}
